﻿//
//  Copyright (c) 2009,2010 Liang Quan Qing.
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
//  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
//  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
//  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//  SUCH DAMAGE.
//
//

#include <stddef.h>
#include "fastconv.h"
#include "fcnv_config.h"
#include "fcnvlocal.h"

// UTF-32 to UTF-8, check valid unicode range
void U32CharToU8Buff_1(DChar_t u32char, unsigned char* pbUtf8Buff, int* pUtf8len, int* pValid)
{
    if ( u32char < 0x80 )               // 7-bit
    {
        *pbUtf8Buff = (unsigned char)u32char;
        *pUtf8len = 1;
        *pValid = 1;
    }
    else if ( u32char < 0x800 )         // 11-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>6)|0xC0);
        *(pbUtf8Buff+1) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 2;
        *pValid = 1;
    }
    else if ( u32char < 0x10000 )       // 16-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>12)|0xE0);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 3;
        *pValid = 1;
    }
    else if ( u32char < 0x200000 )      // 21-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>18)|0xF0);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 12) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+3) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 4;
        *pValid = 1;
    }
    else
    {
        *pbUtf8Buff = (unsigned char)u32char;
        *pUtf8len = 1;
        *pValid = 0;
    }
}

// UTF-32 to UTF-8, not check valid unicode range
void U32CharToU8Buff_2(DChar_t u32char, unsigned char* pbUtf8Buff, int* pUtf8len, int* pValid)
{
    if ( u32char < 0x80 )               // 7-bit
    {
        *pbUtf8Buff = (unsigned char)u32char;
        *pUtf8len = 1;
        *pValid = 1;
    }
    else if ( u32char < 0x800 )         // 11-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>6)|0xC0);
        *(pbUtf8Buff+1) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 2;
        *pValid = 1;
    }
    else if ( u32char < 0x10000 )       // 16-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>12)|0xE0);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 3;
        *pValid = 1;
    }
    else if ( u32char < 0x200000 )      // 21-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>18)|0xF0);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 12) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+3) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 4;
        *pValid = 1;
    }
    else if ( u32char < 0x4000000 )     // 26-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>24)|0xF8);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 18) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) ((u32char >> 12) & 0x3F | 0x80);
        *(pbUtf8Buff+3) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+4) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 5;
        *pValid = 1;
    }
    else if ( u32char < 0x80000000 )    // 31-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>30)|0xFC);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 24) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) ((u32char >> 18) & 0x3F | 0x80);
        *(pbUtf8Buff+3) = (unsigned char) ((u32char >> 12) & 0x3F | 0x80);
        *(pbUtf8Buff+4) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+5) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 6;
        *pValid = 1;
    }
    else
    {
        *pbUtf8Buff = (unsigned char)u32char;
        *pUtf8len = 1;
        *pValid = 0;
    }
}

// Get UTF-8 char size from a UTF-32 char
unsigned GetU8CharSizeFromU32Char(DChar_t u32char)
{
    if ( u32char < 0x80 )          // 7-bit
        return 1;
    if ( u32char < 0x800 )         // 11-bit
        return 2;
    if ( u32char < 0x10000 )       // 16-bit
        return 3;
    if ( u32char < 0x200000 )      // 21-bit
        return 4;
    if ( u32char < 0x4000000 )     // 26-bit
        return 5;
    if ( u32char < 0x80000000 )    // 31-bit
        return 6;
    return 7;
}

// UTF-8 to UTF-32, check valid unicode range
void U8BuffToU32Char_1(const unsigned char* pbUtf8Buff, int* pUtf8len, DChar_t* pU32Char, int* pValid)
{
    const unsigned char *pb = pbUtf8Buff;
    if ( isUTF8Style1(pb[0]) )                              // 1 byte
    {
        *pU32Char = UTF8ToDCharS1(pb[0]);
        *pUtf8len = 1;
        *pValid = 1;
    }
    else if ( isUTF8Style2(pb[0], pb[1]) )                  // 2 bytes
    {
        *pU32Char = UTF8ToDCharS2(pb[0], pb[1]);
        *pUtf8len = 2;
        *pValid = 1;
    }
    else if ( isUTF8Style3(pb[0], pb[1], pb[2]) )           // 3 bytes
    {
        *pU32Char = UTF8ToDCharS3(pb[0], pb[1], pb[2]);
        *pUtf8len = 3;
        *pValid = 1;
    }
    else if ( isUTF8Style4(pb[0], pb[1], pb[2], pb[3]) )    // 4 bytes
    {
        *pU32Char = UTF8ToDCharS4(pb[0], pb[1], pb[2], pb[3]);
        *pUtf8len = 4;
        *pValid = 1;
    }
    else
    {
        *pU32Char = pb[0];
        *pUtf8len = 1;
        *pValid = 0;
    }
}

// UTF-8 to UTF-32, donot check valid unicode range
void U8BuffToU32Char_2(const unsigned char* pbUtf8Buff, int* pUtf8len, DChar_t* pU32Char, int* pValid)
{
    const unsigned char *pb = pbUtf8Buff;
    if ( isUTF8Style1(pb[0]) )                              // 1 byte
    {
        *pU32Char = UTF8ToDCharS1(pb[0]);
        *pUtf8len = 1;
        *pValid = 1;
    }
    else if ( isUTF8Style2(pb[0], pb[1]) )                  // 2 bytes
    {
        *pU32Char = UTF8ToDCharS2(pb[0], pb[1]);
        *pUtf8len = 2;
        *pValid = 1;
    }
    else if ( isUTF8Style3(pb[0], pb[1], pb[2]) )           // 3 bytes
    {
        *pU32Char = UTF8ToDCharS3(pb[0], pb[1], pb[2]);
        *pUtf8len = 3;
        *pValid = 1;
    }
    else if ( isUTF8Style4(pb[0], pb[1], pb[2], pb[3]) )    // 4 bytes
    {
        *pU32Char = UTF8ToDCharS4(pb[0], pb[1], pb[2], pb[3]);
        *pUtf8len = 4;
        *pValid = 1;
    }
    else if ( isUTF8Style5(pb[0], pb[1], pb[2], pb[3], pb[4]) ) // 5 bytes
    {
        *pU32Char = UTF8ToDCharS5(pb[0], pb[1], pb[2], pb[3], pb[4]);
        *pUtf8len = 5;
        *pValid = 1;
    }
    else if ( isUTF8Style6(pb[0], pb[1], pb[2], pb[3], pb[4], pb[5]) )  // 6 bytes
    {
        *pU32Char = UTF8ToDCharS6(pb[0], pb[1], pb[2], pb[3], pb[4], pb[5]);
        *pUtf8len = 6;
        *pValid = 1;
    }
    else
    {
        *pU32Char = pb[0];
        *pUtf8len = 1;
        *pValid = 0;
    }
}

